﻿//
// © Algobox Ltd - Benn Eichhorn
//
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading;

namespace Algobox.Apps.Finance.SterlingTrader
{
    public class OrderAdaptor : Algobox.Interop.SterlingTrader.OrderManagement, Algobox.Feeds.Finance.Trading.ITradingFeedAdaptor
    {
        #region Constructors

        public OrderAdaptor(string feedName)
        {
            FeedName = feedName;
        }

        #endregion
        //////////////////
        #region Member Variables

        public event System.Action OnTradingFeedShutdown;

        private static readonly NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger();

        /// <summary>
        /// Number of milliseconds to hold lock on order update or cancel (1 second)
        /// </summary>
        private const int LOCK_TIMEOUT = 1000;

        private ConcurrentDictionary<string, OrderCallback> _mapOrderEvents = new ConcurrentDictionary<string, OrderCallback>();
        private ConcurrentDictionary<int, string> _mapOrderRecordClientId = new ConcurrentDictionary<int, string>();

        private class OrderCallback
        {
            public OrderCallback(Objects.Finance.Orders.Order order, Feeds.Finance.Trading.IOrderEvent callback, SterlingLib.structSTIOrder orderStruct)
            {
                Order = order;
                Callback = callback;
                StructOrder = orderStruct;
            }
            public Objects.Finance.Orders.Order Order;
            public Feeds.Finance.Trading.IOrderEvent Callback;
            public SterlingLib.structSTIOrder StructOrder;
            public int RecordId;
        }

        #endregion
        //////////////////
        #region Properties

        public string FeedName { get; private set; }

        public bool IsStarted { get; private set; }

        #endregion
        //////////////////
        #region Functions

        public override bool Start()
        {
            return base.Start();
        }

        public override void Stop()
        {
            base.Stop();
        }

        public override void PostProcessSterlingShutdown()
        {
            if (OnTradingFeedShutdown != null)
                OnTradingFeedShutdown();
        }

        public bool TrySubmitOrder(Objects.Finance.Orders.Order order, Feeds.Finance.Trading.IOrderEvent callback, out string message)
        {
            OrderCallback oc = new OrderCallback(order, callback, new SterlingLib.structSTIOrder());

            lock (oc)
            {
                if (_mapOrderEvents.TryAdd(order.OrderIdClient, oc))
                {
                    switch (order.Type)
                    {
                        case Objects.Finance.Orders.Order.OrderType.Limit:
                            oc.StructOrder.bstrClOrderId = order.OrderIdClient;
                            oc.StructOrder.bstrAccount = order.Account;
                            oc.StructOrder.bstrSide = order.IsBuy ? "B" : "S";
                            oc.StructOrder.bstrSymbol = order.Stock.Symbol;
                            oc.StructOrder.nQuantity = (int)order.Quantity;
                            oc.StructOrder.nDisplay = (int)order.Quantity; // must set this otherwise the order will be hidden
                            oc.StructOrder.fLmtPrice = (double)order.Price;
                            oc.StructOrder.nPriceType = (int)PriceType.Limit;
                            oc.StructOrder.bstrTif = ((char)GetSTItif(order.TIF)).ToString();
                            oc.StructOrder.bstrDestination = GetSTIDestination(order.Stock.Exchange);
                            break;

                        default:
                            message = "Unhandled Order Type: " + order.Type.ToString();
                            break;
                    }

                    OrderResultCodes result = SubmitOrder(ref oc.StructOrder);
                    message = result.ToString();

                    if (result == OrderResultCodes.NoError)
                    {
                        Log.Info("{0}|TrySubmitOrder SubmitOrderStruct success send Order({1}) Callback({2})", order.OrderIdClient, order, callback.ToString());
                        order.State = Objects.Finance.Orders.Order.Status.PendingNew;
                        return true;
                    }
                    else
                    {
                        _mapOrderEvents.TryRemove(order.OrderIdClient, out oc);
                        order.Text = "STI Reject: " + result.ToString();
                        Log.Info("{0}|TrySubmitOrder SubmitOrderStruct failed send Order({1}) OrderResultCode({2}-{3}) Callback({4})", order.OrderIdClient, order, (int)result, result, callback.ToString());
                        return false;
                    }
                }
                else
                {
                    Log.Fatal("{0}|TrySubmitOrder attempting to add an existing client order id to order events dictionary! Order({1})", order.OrderIdClient, order);
                }
            }
            order.Text = "TrySubmitOrder could not find ClOrdId(" + order.OrderIdClient + ")";
            Log.Error("{0}|TrySubmitOrder already submited this order, you should call TryUpdateOrder to update an order Order({1})", order.OrderIdClient, order);
            message = "Already submited this order, you should call TryUpdateOrder to update an order";
            return false;
        }

        public bool TrySubmitOrder(Objects.Finance.Orders.Order order, Feeds.Finance.Trading.IOrderEvent callback, Algobox.Objects.Finance.Exchanges.Destinations dest, out string message)
        {
            OrderCallback oc = new OrderCallback(order, callback, new SterlingLib.structSTIOrder());

            lock (oc)
            {
                if (_mapOrderEvents.TryAdd(order.OrderIdClient, oc))
                {
                    switch (order.Type)
                    {
                        case Objects.Finance.Orders.Order.OrderType.Limit:
                            oc.StructOrder.bstrClOrderId = order.OrderIdClient;
                            oc.StructOrder.bstrAccount = order.Account;
                            oc.StructOrder.bstrSide = order.IsBuy ? "B" : "S";
                            oc.StructOrder.bstrSymbol = order.Stock.Symbol;
                            oc.StructOrder.nQuantity = (int)order.Quantity;
                            oc.StructOrder.nDisplay = (int)order.Quantity; // must set this otherwise the order will be hidden
                            oc.StructOrder.fLmtPrice = (double)order.Price;
                            oc.StructOrder.nPriceType = (int)PriceType.Limit;
                            oc.StructOrder.bstrTif = ((char)GetSTItif(order.TIF)).ToString();
                            oc.StructOrder.bstrDestination = GetSTIDestination(dest);
                            break;

                        default:
                            message = "Unhandled Order Type: " + order.Type.ToString();
                            break;
                    }

                    OrderResultCodes result = SubmitOrder(ref oc.StructOrder);
                    message = result.ToString();

                    if (result == OrderResultCodes.NoError)
                    {
                        Log.Info("{0}|TrySubmitOrder SubmitOrderStruct success send Order({1}) Callback({2})", order.OrderIdClient, order, callback.ToString());
                        order.State = Objects.Finance.Orders.Order.Status.PendingNew;
                        return true;
                    }
                    else
                    {
                        _mapOrderEvents.TryRemove(order.OrderIdClient, out oc);
                        order.Text = "STI Reject: " + result.ToString();
                        Log.Info("{0}|TrySubmitOrder SubmitOrderStruct failed send Order({1}) OrderResultCode({2}-{3}) Callback({4})", order.OrderIdClient, order, (int)result, result, callback.ToString());
                        return false;
                    }
                }
                else
                {
                    Log.Fatal("{0}|TrySubmitOrder attempting to add an existing client order id to order events dictionary! Order({1})", order.OrderIdClient, order);
                }
            }
            order.Text = "TrySubmitOrder could not find ClOrdId(" + order.OrderIdClient + ")";
            Log.Error("{0}|TrySubmitOrder already submited this order, you should call TryUpdateOrder to update an order Order({1})", order.OrderIdClient, order);
            message = "Already submited this order, you should call TryUpdateOrder to update an order";
            return false;
        }

        public bool TryReplaceOrder(Objects.Finance.Orders.Order order, out string message, decimal newPrice = 0, uint newQuantity = 0)
        {
            string oldClOrdId = order.OrderIdClient;
            OrderCallback oc;

            if (newPrice == 0 && newQuantity == 0)
            {
                message = "No change - Price and Quantity both equal 0";
                Log.Info("{0}|TryReplaceOrder {1}", oldClOrdId, message, order.ToString());
                return false;
            }
            else
            {
                if (newPrice != 0 && newQuantity != 0 && order.Price == newPrice && order.Quantity == newQuantity)
                {
                    message = "No change - Price and Quantity are same as existing order";
                    Log.Info("{0}|TryReplaceOrder {1} Order({2})", oldClOrdId, message, order.ToString());
                    return false;
                }
                else if (newPrice == 0 && order.Quantity == newQuantity)
                {
                    message = "No change - Price and Quantity are same as existing order";
                    Log.Info("{0}|TryReplaceOrder {1} Order({2})", oldClOrdId, message, order.ToString());
                    return false;
                }
                else if (order.Price == newPrice)
                {
                    message = "No change - Price and Quantity are same as existing order";
                    Log.Info("{0}|TryReplaceOrder {1} Order({2})", oldClOrdId, message, order.ToString());
                    return false;
                }
            }

            if (_mapOrderEvents.TryGetValue(oldClOrdId, out oc))
            {
                if (Monitor.TryEnter(oc, LOCK_TIMEOUT))
                {
                    try
                    {
                        // check we still exist after lock - just to be on the safe side
                        if (_mapOrderEvents.ContainsKey(oldClOrdId))
                        {
                            System.Diagnostics.Debug.Assert(oc.Order == order, "Incoming Order does not match the order in the callback struct");

                            SterlingLib.structSTIOrder stiOrder = oc.StructOrder;

                            if (newPrice != 0)
                                stiOrder.fLmtPrice = (double)newPrice;
                            if (newQuantity != 0)
                            {
                                stiOrder.nQuantity = (int)newQuantity;
                                stiOrder.nDisplay = (int)newQuantity;
                            }

                            // assign a new client order id
                            string newClOrdId = order.GenerateClientOrderId();
                            stiOrder.bstrClOrderId = newClOrdId;

                            Log.Debug("{0}|TryReplaceOrder Adding NewClOrdId({1}) to _mapOrderEvents", oldClOrdId, newClOrdId);
                            _mapOrderEvents.TryAdd(newClOrdId, oc);

                            // send the replace                      
                            OrderResultCodes result;
                            if (oc.StructOrder.bstrClOrderId != null)
                                result = base.ReplaceOrder(ref stiOrder, 0, oldClOrdId);
                            else
                                result = base.ReplaceOrder(ref stiOrder, oc.RecordId, null);

                            message = result.ToString();

                            if (result == OrderResultCodes.NoError)
                            {
                                // on success set new client order id
                                oc.Order.OrderIdClient = newClOrdId;
                                // assign new struct - not entirely necessary though
                                oc.StructOrder = stiOrder;
                                order.State = Objects.Finance.Orders.Order.Status.PendingNew;
                                Log.Info("{0}|TryReplaceOrder ReplaceOrderStruct successfully sent Order({1}) NewPrice({2}) NewQuantity({3})", oldClOrdId, order, newPrice, newQuantity);
                                Log.Debug("{0}|TryReplaceOrder ReplaceOrderStruct removing OldClOrdId({0}) from _mapOrderEvents", oldClOrdId);
                                _mapOrderEvents.TryRemove(oldClOrdId, out oc);
                                return true;
                            }
                            else
                            {
                                Log.Info("{0}|TryReplaceOrder ReplaceOrderStruct failed send Order({1}) NewClOrdId({2}) OrderResultCode({3} {4})", oldClOrdId, order, newClOrdId, (int)result, result);
                                Log.Debug("{0}|TryReplaceOrder ReplaceOrderStruct removing NewClOrdId({0}) from _mapOrderEvents", oldClOrdId, newClOrdId);
                                _mapOrderEvents.TryRemove(newClOrdId, out oc);
                                return false;
                            }
                        }
                    }
                    finally
                    {
                        Monitor.Exit(oc);
                    }
                }
                order.Text = "TryReplaceOrder Couldn't get a lock on order callback";
                Log.Error("{0}|TryReplaceOrder Couldn't get a lock on order callback", oldClOrdId);
                message = "Couldn't get a lock on order callback";
            }
            else
            {
                order.Text = "TryReplaceOrder could not find ClOrdId(" + oldClOrdId + ")";
                Log.Error("{0}|TryReplaceOrder could not find order", oldClOrdId);
                message = "ClientOrderId not mapped";
            }
            return false;
        }

        public bool TryCancelOrder(Objects.Finance.Orders.Order order, out string message)
        {
            string clOrdId = order.OrderIdClient;

            OrderCallback oc;
            if (_mapOrderEvents.TryGetValue(clOrdId, out oc))
            {
                if (Monitor.TryEnter(oc, LOCK_TIMEOUT))
                {
                    try
                    {
                        // check we still exist after lock - just to be on the safe side
                        if (_mapOrderEvents.ContainsKey(clOrdId))
                        {
                            string newClOrdId = oc.Order.GenerateClientOrderId();
                            oc.Order.OrderIdClient = newClOrdId;
                            _mapOrderEvents.TryAdd(newClOrdId, oc);

                            if (oc.StructOrder.bstrClOrderId != null)
                                base.CancelOrder(order.Account, 0, clOrdId, newClOrdId);
                            else
                                base.CancelOrder(order.Account, oc.RecordId, null, newClOrdId);

                            Log.Info("{0}|TryCancelOrder CancelOrder Account({0}) NewClOrdId({2})", clOrdId, order.Account, newClOrdId);
                            message = "PendingCancel";
                            return true;
                        }
                    }
                    finally
                    {
                        Monitor.Exit(oc);
                    }
                }
                Log.Error("{0}|TryCancelOrder Couldn't get a lock on order callback", clOrdId);
                message = "Couldn't get a lock on order callback";
            }
            else
            {
                Log.Error("{0}|TryCancelOrder could not find order ClOrdId({0})", clOrdId);
                message = "ClientOrderId not mapped";
            }
            return false;
        }


        public bool TryCancelOrder(string clientOrderId, out string message)
        {
            OrderCallback oc;
            if (_mapOrderEvents.TryGetValue(clientOrderId, out oc))
            {
                if (Monitor.TryEnter(oc, LOCK_TIMEOUT))
                {
                    try
                    {
                        // check we still exist after lock - just to be on the safe side
                        if (_mapOrderEvents.ContainsKey(clientOrderId))
                        {
                            string oldClOrdId = clientOrderId;
                            string newClOrdId = oc.Order.GenerateClientOrderId();
                            oc.Order.OrderIdClient = newClOrdId;
                            _mapOrderEvents.TryAdd(newClOrdId, oc);

                            if (oc.StructOrder.bstrClOrderId != null)
                                base.CancelOrder(oc.Order.Account, 0, oldClOrdId, newClOrdId);
                            else
                                base.CancelOrder(oc.Order.Account, oc.RecordId, null, newClOrdId);

                            Log.Info("{0}|TryCancelOrder CancelOrder Account({0}) NewClOrdId({2})", oldClOrdId, oc.Order.Account, newClOrdId);
                            message = "PendingCancel";
                            return true;
                        }
                    }
                    finally
                    {
                        Monitor.Exit(oc);
                    }
                }
                Log.Error("{0}|TryCancelOrder Couldn't get a lock on order callback", clientOrderId);
                message = "Couldn't get a lock on order callback";
            }
            else
            {
                Log.Error("{0}|TryCancelOrder could not find order ClOrdId({0})", clientOrderId);
                message = "ClientOrderId not mapped";
            }
            return false;
        }


        public bool TryGetOrder(string clientOrderId, out Objects.Finance.Orders.Order order)
        {
            throw new NotImplementedException();
        }

        public override void PostProcessOrder(ref SterlingLib.structSTIOrderUpdate order)
        {
            bool frontEndUpdate = false;

            string clOrdId = order.bstrClOrderId;

            if (clOrdId == null)
            {
                frontEndUpdate = true;
                if (!_mapOrderRecordClientId.TryGetValue(order.nOrderRecordId, out clOrdId))
                    return;
            }

            Objects.Finance.Orders.Order.Status state = GetOrderStatus((OrderStatus)order.nOrderStatus);

            OrderCallback callback;
            if (_mapOrderEvents.TryGetValue(clOrdId, out callback))
            {
                lock (callback)
                {
                    if (_mapOrderEvents.ContainsKey(clOrdId))
                    {
                        if (frontEndUpdate)
                        {
                            callback.StructOrder.bstrClOrderId = null;
                        }
                        else
                        {
                            callback.StructOrder.bstrClOrderId = order.bstrClOrderId;
                            _mapOrderRecordClientId.AddOrUpdate(order.nOrderRecordId, clOrdId, (key, value) => { return clOrdId; });
                        }
                        callback.RecordId = order.nOrderRecordId;                        

                        if (state == Objects.Finance.Orders.Order.Status.Replaced)
                        {
                            callback.Order.Price = (decimal)order.fLmtPrice;
                            callback.Order.Quantity = (uint)order.nQuantity;
                        }

                        // update the order with latest data
                        callback.Order.PriceAverage = (decimal)order.fAvgExecPrice;
                        callback.Order.QuantityFilled = (uint)order.nCumExecQuantity;
                        callback.Order.QuantityRemaining = (uint)order.nLvsQuantity;
                        callback.Order.OrderIdExchange = order.bstrExchOrderId;
                        callback.Order.OrderIdHost = order.nOrderRecordId.ToString();
                        callback.Order.TimeUpdated = DateTime.ParseExact(order.bstrUpdateTime, "yyyyMMddHHmmss", System.Globalization.CultureInfo.InvariantCulture);
                        callback.Order.Text = order.bstrLogMessage;
                        
                        if(Log.IsInfoEnabled)
                            Log.Info("{0}|PostProcessOrder OnOrderUpdate OrderStatus({1} {2}) RecId({3}) Message({4})", clOrdId, order.nOrderStatus, state, order.nOrderRecordId, order.bstrLogMessage);

                        // send the order update
                        callback.Callback.OrderUpdate(callback.Order, state);

                        OrderCallback oldCallback;
                        string oldClientOrderId;

                        switch (state)
                        {
                            case Objects.Finance.Orders.Order.Status.Replaced:
                                // remove old ClientOrderId reference
                                if (!frontEndUpdate && callback.Order.OrderIdClientPrevious != null)
                                {
                                    _mapOrderEvents.TryRemove(callback.Order.OrderIdClientPrevious, out oldCallback);
                                }
                                break;

                            case Objects.Finance.Orders.Order.Status.Canceled:
                                _mapOrderRecordClientId.TryRemove(order.nOrderRecordId, out oldClientOrderId);
                                _mapOrderEvents.TryRemove(clOrdId, out oldCallback);
                                if (!frontEndUpdate && callback.Order.OrderIdClientPrevious != null)
                                {
                                    _mapOrderEvents.TryRemove(callback.Order.OrderIdClientPrevious, out oldCallback);
                                }
                                break;

                            case Objects.Finance.Orders.Order.Status.Filled:
                            case Objects.Finance.Orders.Order.Status.Stopped:
                                _mapOrderEvents.TryRemove(clOrdId, out oldCallback);
                                _mapOrderRecordClientId.TryRemove(order.nOrderRecordId, out oldClientOrderId);
                                break;

                            case Objects.Finance.Orders.Order.Status.Rejected:
                                // if rejected a pending replace is the order still live in the market? need to check this
                                // ideally we would like any order that is rejected to mean that it is canceled also
                                if (callback.Order.State == Objects.Finance.Orders.Order.Status.PendingReplace)
                                    Log.Error("{0}|PostProcessOrder OnOrderUpdate Recieved a reject for a pending replace (just checking to see if this happens) Order({1})", clOrdId, callback.Order.ToString());
                                else
                                {
                                    _mapOrderEvents.TryRemove(callback.Order.OrderIdClient, out oldCallback);
                                }
                                _mapOrderRecordClientId.TryRemove(order.nOrderRecordId, out oldClientOrderId);
                                break;
                        }

                        // update the order status to reflect the latest status
                        callback.Order.State = state;
                    }
                }
            }
            else
            {
                if(Log.IsTraceEnabled)
                    Log.Trace("{2}|PostProcessOrder OnOrderUpdate Unknown order OrderStatus({0} {1}) RecId({3}) Message({4})", order.nOrderStatus, state, order.bstrClOrderId, order.nOrderRecordId, order.bstrLogMessage);
            }
        }

        public override void PostProcessOrder(ref SterlingLib.structSTIOrderReject order)
        {
            if (order.bstrClOrderId == null)
                return;

            OrderCallback callback;
            if (_mapOrderEvents.TryRemove(order.bstrClOrderId, out callback))
            {
                lock (callback)
                {
                    Objects.Finance.Orders.Order.Status state = Objects.Finance.Orders.Order.Status.Rejected;

                    // update the order with latest data
                    callback.Order.TimeUpdated = DateTime.Now;

                    RejectReason reason = (RejectReason)order.nRejectReason;

                    Log.Info("{0}|PostProcessOrder OnOrderReject OrderStatus({1} {2}) Text({3}) Order({4}) OldClOrdId({5})", order.bstrClOrderId, order.nRejectReason, reason, order.bstrText, callback.Order.ToString(), callback.Order.OrderIdClientPrevious);

                    callback.Order.Text = reason.ToString() + " - " + order.bstrText;

                    // send the order update
                    callback.Callback.OrderUpdate(callback.Order, state);

                    // update the order status to reflect the latest status
                    callback.Order.State = state;                    
                }
            }
            else
            {
                if (Log.IsTraceEnabled)
                {
                    RejectReason reason = (RejectReason)order.nRejectReason;
                    Log.Trace("{0}|PostProcessOrder OnOrderReject Unknown order OrderStatus({1} {2}) Text({3})", order.bstrClOrderId, order.nRejectReason, reason, order.bstrText);
                }
            }
        }

        #endregion
        //////////////////
        #region Enums

        private Objects.Finance.Orders.Order.Status GetOrderStatus(OrderStatus status)
        {
            switch (status)
            {
                case OrderStatus.Filled:
                    return Objects.Finance.Orders.Order.Status.Filled;
                case OrderStatus.Stopped:
                    return Objects.Finance.Orders.Order.Status.Stopped;
                case OrderStatus.Canceled:
                case OrderStatus.Expired:
                case OrderStatus.Suppended:
                case OrderStatus.DoneForDay:
                    return Objects.Finance.Orders.Order.Status.Canceled;
                case OrderStatus.PartiallyFilled:
                    return Objects.Finance.Orders.Order.Status.FilledPartial;
                case OrderStatus.Replaced:
                    return Objects.Finance.Orders.Order.Status.Replaced;
                case OrderStatus.Rejected:
                    return Objects.Finance.Orders.Order.Status.Rejected;
                case OrderStatus.New:
                case OrderStatus.AcceptedForBidding:
                    return Objects.Finance.Orders.Order.Status.New;
                case OrderStatus.PendingCancel:
                    return Objects.Finance.Orders.Order.Status.PendingCancel;
                case OrderStatus.PendingNew:
                    return Objects.Finance.Orders.Order.Status.PendingNew;
                case OrderStatus.PendingReplace:
                    return Objects.Finance.Orders.Order.Status.PendingReplace;
                case OrderStatus.Calculated:
                case OrderStatus.Adjusted:
                case OrderStatus.Statused:
                    return Objects.Finance.Orders.Order.Status.Updated;
                case OrderStatus.Unknown:
                default:
                    return Objects.Finance.Orders.Order.Status.Unknown;
            }
        }

        private PriceType GetSTIPriceType(Objects.Finance.Orders.Order.OrderType orderType)
        {
            switch (orderType)
            {
                case Objects.Finance.Orders.Order.OrderType.Limit:
                    return PriceType.Limit;
                case Objects.Finance.Orders.Order.OrderType.Market:
                    return PriceType.Market;
                case Objects.Finance.Orders.Order.OrderType.ServerSideStopLimit:
                    return PriceType.ServerSideStopLimitOrder;
                default:
                    return PriceType.Limit;
            }
        }

        private TIF GetSTItif(Objects.Finance.Orders.Order.TimeInForce tif)
        {
            switch (tif)
            {
                case Objects.Finance.Orders.Order.TimeInForce.DAY:
                    return TIF.DAY;
                case Objects.Finance.Orders.Order.TimeInForce.FOK:
                    return TIF.FOK;
                case Objects.Finance.Orders.Order.TimeInForce.IOC:
                    return TIF.IOC;
                default:
                    return TIF.DAY;
            }
        }


        private string GetSTIDestination(Objects.Finance.Exchanges.ExchangeMIC exchangeMIC)
        {
            switch (exchangeMIC)
            {
                case Objects.Finance.Exchanges.ExchangeMIC.CHIX:
                    return "CHIXEU";
                case Objects.Finance.Exchanges.ExchangeMIC.BATE:
                    return "BATSEU";
                case Objects.Finance.Exchanges.ExchangeMIC.XLON:
                    return "LSE";
                case Objects.Finance.Exchanges.ExchangeMIC.XNAS:
                    return "NASD";
                case Objects.Finance.Exchanges.ExchangeMIC.ARCX:
                    return "ARCA";
                case Objects.Finance.Exchanges.ExchangeMIC.BATS:
                    return "BATS";
                case Objects.Finance.Exchanges.ExchangeMIC.XNYS:
                    return "NYSE";
                case Objects.Finance.Exchanges.ExchangeMIC.EDGA:
                    return "EDGX";
                case Objects.Finance.Exchanges.ExchangeMIC.EDGX:
                    return "EDGX";
                default:
                    return "NASD";
            }
        }


        private string GetSTIDestination(Objects.Finance.Exchanges.Destinations dest)
        {
            switch (dest)
            {
                case Objects.Finance.Exchanges.Destinations.CHIX:
                    return "CHIXEU";
                case Objects.Finance.Exchanges.Destinations.BATE:
                    return "BATSEU";
                case Objects.Finance.Exchanges.Destinations.XLON:
                    return "LSE";
                default:
                    return "BATSCYCLE";
            }
        }

        private string GetSTIListingExcahnge(Objects.Finance.Exchanges.ExchangeMIC exchangeMIC)
        {
            switch (exchangeMIC)
            {
                case Objects.Finance.Exchanges.ExchangeMIC.CHIX:
                    return "";
                case Objects.Finance.Exchanges.ExchangeMIC.BATE:
                    return "";
                case Objects.Finance.Exchanges.ExchangeMIC.XLON:
                    return "";
                case Objects.Finance.Exchanges.ExchangeMIC.XNAS:
                    return "Q";
                case Objects.Finance.Exchanges.ExchangeMIC.ARCX:
                    return "P";
                case Objects.Finance.Exchanges.ExchangeMIC.BATS:
                    return "B";
                case Objects.Finance.Exchanges.ExchangeMIC.XNYS:
                    return "N";
                default:
                    return "";
            }
        }

        #endregion


    }
}
